/**
 * Copyright (c) 2010
 * Bert Young. UESTC. 
 */

#ifndef BERT_TCPCLIENTTASK_H
#define BERT_TCPCLIENTTASK_H

#include "SocketConnection.h"
#include "Timer.h"
#include "Entry.h"
#include "MsgBuffer.h"

class TCPClientTask : public Entry
{
protected:
	/**
	 * The real connection
	 */
	SocketConnection * pSocket;
	/**
	 * Buffered or nonbuffer
	 */
	bool buffered;
	/**
	 * 连接到什么类型的服务器？
	 */
	unsigned int taskType;
	/**
	 * 连接到服务器IP
	 */
	std::string serverIP;
	/**
	 * 连接到服务器PORT
	 */
	unsigned short serverPort;
public:
	bool connect( const char * servip, const unsigned short port)
	{
		this->serverIP = servip;
		this->serverPort = port;

		int fd = socket(AF_INET, SOCK_STREAM, 0 );
		if ( fd < 0 )
			return false;

		socklen_t winsize = 128 * 1024;
		int ret = ::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &winsize, sizeof(winsize));
		if ( ret < 0 )
		{
			TEMP_FAILURE_RETRY(::close(fd));
			return false;
		}
		ret = ::setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &winsize, sizeof(winsize));
		if ( ret < 0 )
		{
			TEMP_FAILURE_RETRY(::close(fd));
			return false;
		}

		struct sockaddr_in serv;
		bzero(&serv, sizeof(serv));
		serv.sin_family = AF_INET;
		serv.sin_addr.s_addr = inet_addr(serverIP.c_str());
		serv.sin_port = htons(port);

		ret = TEMP_FAILURE_RETRY(::connect(fd,(struct sockaddr*)&serv,sizeof(serv)));
		if ( ret < 0 )
		{
			TEMP_FAILURE_RETRY(::close(fd));
			return false;
		}

		this->pSocket = new SocketConnection(fd, &serv);
		if ( pSocket == NULL )
		{
			TEMP_FAILURE_RETRY(::close(fd));
			return false;
		}
		//connect server success!
		return ret;
	}

	/**
	 * State of this connection
	 */
	enum ConnectionState
	{
		idle,
		verify,
		okay,
		recycle,
	}state;
	/**
	 * Should be disconnected
	 */
	enum DisconnectType
	{
		disconnect_none,
		disconnect_sock_error,
	}disconnect;
private:
	/**
	 * Is the remote client pass through
	 */
	bool verified;
	/**
	 * When this connection is created
	 */
	Time bornTime;
	/**
	 * The timer of heartbeat tick
	 */
	Timer hearbeatTimer;
	/**
	 * Our heartbeat tick is sent
	 */
	bool beatSent;
public:
	/**
	 * Constructor
	 */
	TCPClientTask(int localfd = -1, const struct sockaddr_in * client=NULL, bool buffer = false)
	{
		isEpollRead = false;
	}
	/**
	 * Destructor
	 */
	virtual ~TCPClientTask()
	{
		delete pSocket;
		pSocket = NULL;
	}
	bool checkVerifyTimeout(const Time & now, unsigned long out = 4000L) const
	{
		return now.msec() - bornTime.msec() > out;
	}
	virtual int verifyConn()
	{
		// recvtobuf_nopoll
		// recvtocmd_nopoll
		// error -1 ;    0 timeout
		return 1;
	}
	virtual int recycleConn()
	{
		return 1;
	}
	bool checkHearbeat(const Time & now)
	{
		if ( hearbeatTimer(now) )
		{
			// 在listenrecv中，收到测试指令就清beatSent
			if ( beatSent )
				return false;// should abort this task
			else
			{
				//if (this->socket.sendPacket(testCmd) )
				//send client heartbeat
				if (true )
					beatSent = true;
			}
		}
		return true;
	}
	/**
	 * set state
	 */
	void nextState()
	{
		ConnectionState oldstate = this->state;
		switch(oldstate)
		{
			case idle:
				this->state = verify;
				break;
			case verify:
				this->state = okay;//addtocontainer
				break;
			case okay:
				//removecontainer
				this->state = recycle;
				break;
			case recycle:
				resetState();
				break;
		}
	}
	/**
	 * reset state
	 */
	void resetState()
	{
		bornTime.now();
		this->state = idle;
		delete pSocket;
		this->pSocket = NULL;
		this->disconnect = disconnect_none;
	}

	/**
	 * Send cmd
	 */
	bool sendPacket(const void * cmd, int len)
	{
		if ( pSocket == NULL )
			return false;
		else
			return pSocket->sendPacket(cmd, len, buffered);
	}
	/**
	 * Receive data
	 */
	bool recv(bool needRecv = true)
	{// OKAY thread call this function
		int nBytes = 0;
		if ( needRecv )//epoll_wait success, just ::recv
			nBytes = this->pSocket.recv();
		if ( nBytes < 0 )
			return false;

		unsigned char cmd[SocketConnection::MAX_DATASIZE];
		while ( true )// 将应用缓冲的消息取干净
		{
			nBytes = this->socket.recvPacketNoPoll(cmd);

			if ( nBytes > 0 )
			{
#if 0
				if ( cmd == TEST )
				{
			如果收到我发出的信号，那是服务器echo的，clear beat；
			如果收到服务器测试信号，我echo它
					if ( cmd == RETURN_FROM_CLIENT )
						this->beatSent = false;
					else if ( cmd == TEST_FROM_CLIENT)
						this->sendPacket();
					else
						//wrong cmd;
				}
				else
#endif
				{
					// add to msgqueue, or msgparse it
					parse( cmd, nBytes );
				}
			}
			else
			{
				return true;
			}
		}
		return true;
	}
	/**
	 * Send data
	 */
	bool send()
	{
		if ( pSocket == NULL )
			return false;
		else
			return pSocket->sync();
	}
	/**
	 * Parse the cmd or put it into buffer for logical thread to parse it
	 */
	virtual void parse(const void * cmd, int len) //= 0;
	{
	}
	/**
	 * For epoll me
	 */
	void epollAdd(int epfd, __uint32_t events, void * ptr )
	{
		this->socket.epollAdd(epfd, events, ptr);
	}

	void epollDel( int epfd )
	{
		this->socket.epollDel(epfd);
	}

	unsigned int getID() const
	{
		return Entry::id;
	}
	void setDisconnect(DisconnectType type)
	{
		this->disconnect = type;
	}
	bool shouldDisconnect()
	{
		return this->disconnect == disconnect_none;
	}

	bool setBuffer(bool buffer = true)
	{
		return this->buffer = buffer;
	}
	//在验证线程通过了verifyConn之后，再添加到SUPERTASKMANAGER
	virtual bool uniqueAdd()
	{
		return true;
	}
	//在回首线程通过了recycleConn之后，再移出SUPERTASKMANAGER
	virtual bool uniqueDel()
	{
		return true;
	}
	bool unique;
	void setUnique()
	{
		unique = true;
	}
	virtual int waitSync()
	{
		return 1;
	}
	/**
	 * Is epolled for read
	 */
	bool isEpollRead;
};

class TCPClientTaskNoBuffer : public TCPClientTask, public ParseMsg 
{
public:
	TCPClientTaskNoBuffer(int localfd, const struct sockaddr_in * client=NULL, bool buffer=false):TCPTask(localfd,client,buffer)
	{
	}
	virtual void parse(const void * cmd, int len)
	{
		msgParse(cmd, len);
	}

};

class TCPClientTaskBuffer: public TCPClientTask, public MsgBuffer
{
public:
	virtual void parse(const void * cmd, int len)
	{
		MsgBuffer::put(cmd, len);
	}

};

#endif

